package ouc.pkere.utils.generator.codegen;

import ouc.pkere.utils.generator.utils.CommonUtils;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

/**
 * Entity代码生成器：数据库表SQL - JavaBean .java
 * <p>
 * -- bean属性按原始数据库字段经过去掉下划线，并大写处理首字母等
 * -- 生成的JavaBean带数据库的字段说明
 */
public class EntityJavaCodeGenerator {

    private String tableName = "";
    private String packageName = ""; // *.*.*
    private String packagePath = ""; // /*/*/*
    private String entityName = ""; // *Entity

    private String[] rawColNames;
    private String[] colNames;
    private String[] colTypes;
    private int[] colSizes; // 列名大小
    private int[] colScale; // 列名小数精度
    private String primaryKeyTypeStr; // 主键类型字符串


    private boolean importUtil = false;
    private boolean importSql = false;
    private boolean importMath = false;

    // Spring Entity
    private boolean importPersistence = true;

    // 是否生成可序列化的类
    private boolean toSerializable = false;


    /**
     * 数据库表转实体
     *
     * @param tName
     */
    public String tableToEntity(String tName, String pkgName, String pkgPath, String enName) {

        tableName = tName;
        packageName = pkgName;
        packagePath = pkgPath;
        entityName = enName;

        // 数据连Connection获取
        Connection conn = getConnection();

        // 读一行记录
        String strSql = "SELECT * FROM " + tableName + " LIMIT 10"; // + " WHERE id=1"

        try {
            System.out.println(strSql);
            PreparedStatement pstmt = conn.prepareStatement(strSql);
            pstmt.executeQuery();
            ResultSetMetaData rsmd = pstmt.getMetaData();

            int size = rsmd.getColumnCount(); // 共有多少列

            rawColNames = new String[size];
            colNames = new String[size];
            colTypes = new String[size];
            colSizes = new int[size];
            colScale = new int[size];

            for (int i = 0; i < rsmd.getColumnCount(); i++) {

                System.out.println("第 " + (i + 1) + " 列的列名是：" + rsmd.getColumnName(i + 1));

                rawColNames[i] = rsmd.getColumnName(i + 1);
                colNames[i] = rsmd.getColumnName(i + 1).toLowerCase();
                colTypes[i] = rsmd.getColumnTypeName(i + 1).toLowerCase();
                colScale[i] = rsmd.getScale(i + 1);


                if ("datetime".equals(colTypes[i]) || "date".equals(colTypes[i])) {
                    importUtil = true;
                }
                if ("image".equals(colTypes[i]) || "text".equals(colTypes[i])) {
                    importSql = true;
                }
                if (colScale[i] > 0) {
                    importMath = true;
                }

                colSizes[i] = rsmd.getPrecision(i + 1);
            }

            String content = parse(colNames, colTypes, colSizes);

            System.out.println(content);



            // Entity的完整生成路径包含entity子目录
            String entityFullPath = packagePath + File.separator + "entity";

            File entityFullPathOp = new File(entityFullPath);

            if (!entityFullPathOp.exists()) {
                entityFullPathOp.mkdir();
            }

            String entityJavaFilePathName =entityFullPath + File.separator + entityName + ".java";
            System.out.println(entityJavaFilePathName);

            // 将所生成的代码保存至Java源代码文件
            CommonUtils.codeToSrcFile(entityJavaFilePathName, content);

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            releaseConnection(conn);
        }

        return primaryKeyTypeStr;
    }

    /**
     * 解析处理(生成实体类主体代码)
     */

    private String parse(String[] colNames, String[] colTypes, int[] colSizes) {

        StringBuffer sb = new StringBuffer();
        sb.append("package " + packageName + ".entity;\r\n\r\n");

        if (toSerializable) {
            sb.append("\r\nimport java.io.Serializable;\r\n");
        }
        if (importUtil) {
            sb.append("import java.utils.Date;\r\n");
        }
        if (importSql) {
            sb.append("import java.sql.*;\r\n\r\n");
        }
        if (importMath) {
            sb.append("import java.math.*;\r\n\r\n");
        }
        if (importPersistence) {
            sb.append("import javax.persistence.*;\r\n\r\n");
        }

        // 表注释
        processColnames(sb);

        // 生成实体注解
        sb.append("@Entity\n");

        // 生成表注解
        sb.append("@Table(name = \"" + tableName + "\")\r\n");

        // 生成类声明
        sb.append("public class " + entityName);

        // 是否支持序列化
        if (toSerializable) {
            sb.append(" implements Serializable {\r\n");
        } else {
            sb.append(" {\r\n");
        }



        processAllAttrs(sb);

        processAllMethod(sb);

        sb.append("}\r\n");

        System.out.println(sb.toString());

        return sb.toString();
    }

    /**
     * 处理列名,把空格下划线'_'去掉,同时把下划线后的首字母大写 要是整个列在3个字符及以内,则去掉'_'后,不把"_"后首字母大写.
     * 同时把数据库列名,列类型写到注释中以便查看,
     *
     * @param sb
     */
    private void processColnames(StringBuffer sb) {
        sb.append("\r\n/** " + tableName + "\r\n");
        String colsiz = "";
        for (int i = 0; i < colNames.length; i++) {
            colsiz = colSizes[i] <= 0 ? "" : (colScale[i] <= 0 ? "("
                    + colSizes[i] + ")" : "(" + colSizes[i] + "," + colScale[i]
                    + ")");
            sb.append("\t" + colNames[i].toUpperCase() + " "
                    + colTypes[i].toUpperCase() + colsiz + "\r\n");
            char[] ch = colNames[i].toCharArray();
            char c = 'a';
            if (ch.length > 3) {
                for (int j = 0; j < ch.length; j++) {
                    c = ch[j];
                    if (c == '_') {
                        if (ch[j + 1] >= 'a' && ch[j + 1] <= 'z') {
                            ch[j + 1] = (char) (ch[j + 1] - 32);
                        }
                    }
                }
            }
            String str = new String(ch);
            colNames[i] = str.replaceAll("_", "");
        }
        sb.append("*/\r\n");
    }

    /**
     * 生成所有的方法
     *
     * @param sb
     */
    private void processAllMethod(StringBuffer sb) {
        for (int i = 0; i < colNames.length; i++) {
            sb.append("\tpublic void set"
                    + initcap(colNames[i])
                    + "("
                    + mysqlSqlType2JavaType(colTypes[i], colScale[i],
                    colSizes[i]) + " " + colNames[i] + "){\r\n");
            sb.append("\t\tthis." + colNames[i] + " = " + colNames[i] + ";\r\n");
            sb.append("\t}\r\n");

            sb.append("\tpublic "
                    + mysqlSqlType2JavaType(colTypes[i], colScale[i],
                    colSizes[i]) + " get" + initcap(colNames[i])
                    + "(){\r\n");
            sb.append("\t\treturn " + colNames[i] + ";\r\n");
            sb.append("\t}\r\n");
        }
    }

    /**
     * 解析输出属性
     *
     * @return
     */
    private void processAllAttrs(StringBuffer sb) {

        if (toSerializable) {
            sb.append("\tprivate static final long serialVersionUID = 1L;\r\n");
        }

        for (int i = 0; i < colNames.length; i++) {

            if (colNames[i].equals("id")) {
                // 设置主键和自增策略，后紧跟属性必须对应与当前表的主键
                sb.append("\t@Id\n" + "\t@GeneratedValue\n");
                sb.append("\t@Column(name = \"" + rawColNames[i] + "\")" + "\r\n");
                sb.append("\tprivate "
                        + mysqlSqlType2JavaType(colTypes[i], colScale[i],
                        colSizes[i]) + " " + colNames[i] + ";\r\n\r\n");

                // 记录主键类型字符串，并返回，以便生成Repository时使用
                primaryKeyTypeStr =  mysqlSqlType2JavaType(colTypes[i], colScale[i], colSizes[i]);

            } else {
                sb.append("\t@Column(name = \"" + rawColNames[i] + "\")" + "\r\n");
                sb.append("\tprivate "
                        + mysqlSqlType2JavaType(colTypes[i], colScale[i],
                        colSizes[i]) + " " + colNames[i] + ";\r\n\r\n");
            }
        }
        sb.append("\r\n");
    }

    /**
     * 把输入字符串的首字母改成大写
     *
     * @param str
     * @return
     */
    private String initcap(String str) {
        char[] ch = str.toCharArray();
        if (ch[0] >= 'a' && ch[0] <= 'z') {
            ch[0] = (char) (ch[0] - 32);
        }
        return new String(ch);
    }

//    /* ***************************************************************************** */
//    /**
//     * Oracle
//     *
//     * @param sqlType
//     * @param scale
//     * @return
//     */
//    private String oracleSqlType2JavaType(String sqlType, int scale, int size) {
//        if (sqlType.equals("integer")) {
//            return "Integer";
//        } else if (sqlType.equals("long")) {
//            return "Long";
//        } else if (sqlType.equals("float") || sqlType.equals("float precision"))
//            return "float";
//        else if (sqlType.equals("double") || sqlType.equals("double precision")) {
//            return "Double";
//        } else if (sqlType.equals("number") || sqlType.equals("decimal")
//                || sqlType.equals("numeric") || sqlType.equals("real")) {
//            return scale == 0 ? (size < 10 ? "Integer" : "Long") : "BigDecimal";
//        } else if (sqlType.equals("varchar") || sqlType.equals("varchar2")
//                || sqlType.equals("char") || sqlType.equals("nvarchar")
//                || sqlType.equals("nchar")) {
//            return "String";
//        } else if (sqlType.equals("datetime") || sqlType.equals("date")
//                || sqlType.equals("timestamp")) {
//            return "Date";
//        }
//
//        return null;
//    }
      /* ***************************************************************************** */

    /**
     * MySQL类型转换为Java类型
     *
     * @param sqlType
     * @param scale
     * @return
     */
    private String mysqlSqlType2JavaType(String sqlType, int scale, int size) {

        if (sqlType.equals("int") || sqlType.equals("int unsigned")) {
            return "Integer";
        } else if (sqlType.equals("bigint") || sqlType.equals("bigint unsigned")) {
            return "Long";
        } else if (sqlType.equals("float") || sqlType.equals("float unsigned"))
            return "float";
        else if (sqlType.equals("double") || sqlType.equals("double unsigned")) {
            return "Double";
        } else if (sqlType.equals("number") || sqlType.equals("decimal")
                || sqlType.equals("numeric") || sqlType.equals("real")) {
            return scale == 0 ? (size < 10 ? "Integer" : "Long") : "BigDecimal";
        } else if (sqlType.equals("varchar") || sqlType.equals("varchar2")
                || sqlType.equals("char") || sqlType.equals("nvarchar")
                || sqlType.equals("nchar")) {
            return "String";
        } else if (sqlType.equals("datetime") || sqlType.equals("date")
                || sqlType.equals("timestamp")) {
            return "Date";
        }
        return null;
    }

    /**
     * 自动获取JDBC链接
     */
    protected static Connection getConnection() {
        Connection localConnection = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            //MARK
            localConnection = DriverManager.getConnection(
                    "jdbc:mysql://127.0.0.1:3306/pkdb?useUnicode=true&characterEncoding=UTF-8", "root", "root");
            localConnection.setAutoCommit(false);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return localConnection;
    }

    /**
     * 释放jdbc链接
     *
     * @param conn
     */
    protected static void releaseConnection(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        //EntityJavaCodeGenerator t = new EntityJavaCodeGenerator();
        //t.tableToEntity("ch_basic_info_xlt_info");
    }
}